#include "mem.h"
#include "async.h"
#include "thread.h"

typedef struct _func_t func_t;
typedef struct _func_t
{
	func_t				*prev;
	func_t				*next;
	void				*data;
	RedAsyncFunction	 func;
} func_t;

static func_t funcs;
static RedSpin spinlock;
static RedThread *thread;
static RedSemaphore semaphore;

static RedObject *dispatch_thread(RedObject *args)
{
	func_t *node = NULL;

	for (;;)
	{
		/* waiting for pending calls */
		RedSemaphore_Wait(&semaphore);

		/* empty queue implies that we should stop looping */
		if (funcs.next == &funcs)
			return NULL;

		/* fetch a asynchronous function from queue */
		RedSpin_Acquire(&spinlock);
		node = funcs.next;
		node->prev->next = node->next;
		node->next->prev = node->prev;
		RedSpin_Release(&spinlock);

		/* call the handler function */
		node->func(node->data);
		RedMem_Free(node);
	}
}

void Red_FreeAsyncQueue(void)
{
	/* issue an empty function to identify the end of thread */
	RedSemaphore_Signal(&semaphore);

	/* and release all resources */
	RedThread_Join(thread);
	RedSemaphore_Free(&semaphore);
}

char Red_InitAsyncQueue(void)
{
	/* initialize doubly-linked list */
	funcs.prev = &funcs;
	funcs.next = &funcs;

	/* initialize semaphore, then start the dispatcher thread */
	RedSemaphore_Init(&semaphore, 0);
	thread = RedThread_Create(dispatch_thread, NULL, 0);
	return 1;
}

void RedAsync_Schedule(RedAsyncFunction func, void *data)
{
	func_t *node = RedMem_Alloc(sizeof(func_t));

	node->data = data;
	node->func = func;
	node->next = &funcs;

	RedSpin_Acquire(&spinlock);
	node->prev = funcs.prev;
	funcs.prev->next = node;
	funcs.prev = node;
	RedSpin_Release(&spinlock);
	RedSemaphore_Signal(&semaphore);
}
